#ifndef __PMM_H__
#define __PMM_H__

#include <common.h>
#include <list.h>
#include <spinlock.h>

#define KERNBASE	(0x00000000UL)
#define PHYSTOP		(KERNBASE + 128 * 1024 * 1024)
#define KERNSIZE	(PHYSTOP - KERNBASE)
#define PGSIZE		4096
#define PGSHIFT		12
#define NCPU			8

struct page {
  struct {
    unsigned inused: 16;
    unsigned objects: 15;
    unsigned frozen: 1;
  };
  spinlock_t lock;
  struct slab *slab;
  void *freelist;
  struct list_head list;
};

struct slab_cpu {
  struct page *page;
  struct list_head partial;
};

struct slab {
  unsigned size;
  unsigned offset;
  unsigned objects;
  struct slab_cpu cpu_slab[NCPU];
};

struct free_area {
  spinlock_t lock;
  struct list_head free_list;
  size_t nr_free;
};

extern struct page *pages;
extern size_t npage;

#define PPN(pa)	(((uintptr_t)pa - (uintptr_t)KERNBASE) >> PGSHIFT)

static inline size_t page2ppn(struct page *page) {
  return page - pages;
}

static inline struct page *pa2page(uintptr_t pa) {
  assert(PPN(pa) < npage);
  return &pages[PPN(pa)];
}

static inline uintptr_t page2pa(struct page *page) {
  return (page2ppn(page) << PGSHIFT) + (uintptr_t)KERNBASE;
}

#endif